package com.example.imagesteganographylibrary.Utils;

import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;
import java.util.ArrayList;
import java.util.List;

public class Utility {
    private static final int SQUARE_BLOCK_SIZE = 512;

    /**
     * This method calculates the number of square block needed
     *
     * @param pixels int
     * @return number of Square blocks {Integer}
     */
    public static int squareBlockNeeded(int pixels) {
        int result;

        int quadratic = SQUARE_BLOCK_SIZE * SQUARE_BLOCK_SIZE;
        int divisor = pixels / (quadratic);
        int remainder = pixels % (quadratic);

        result = divisor + (remainder > 0 ? 1 : 0);

        return result;
    }

    /**
     * This method splits the image into many images of ( SQUARE_BLOCK_SIZE * SQUARE_BLOCK_SIZE ) size.
     *
     * @param pixelmap pixelmap
     * @return List of splitted images {List}
     */
    public static List<MergePixelMap.PixelMapSlice> splitImage(PixelMap pixelmap) {

        //For height and width of the small image chunks
        int chunkHeight, chunkWidth;

        //To store all the small image chunks in bitmap format in this list
        ArrayList<MergePixelMap.PixelMapSlice> chunkedImages = new ArrayList<>();

        // Assume like a matrix in which the element is a Small Square block
        //Rows and columns of that matrix
        int rows = pixelmap.getImageInfo().size.height / SQUARE_BLOCK_SIZE;
        int cols = pixelmap.getImageInfo().size.width / SQUARE_BLOCK_SIZE;

        int chunk_height_mod = pixelmap.getImageInfo().size.height % SQUARE_BLOCK_SIZE;
        int chunk_width_mod = pixelmap.getImageInfo().size.width % SQUARE_BLOCK_SIZE;
        if (chunk_height_mod > 0)
            rows++;
        if (chunk_width_mod > 0)
            cols++;
        int y_coordinate = 0;

        for (int x = 0; x < rows; x++) {

            int x_coordinate = 0;

            for (int y = 0; y < cols; y++) {

                chunkHeight = SQUARE_BLOCK_SIZE;
                chunkWidth = SQUARE_BLOCK_SIZE;

                if (y == cols - 1 && chunk_width_mod > 0)
                    chunkWidth = chunk_width_mod;

                if (x == rows - 1 && chunk_height_mod > 0)
                    chunkHeight = chunk_height_mod;


                //Adding chunk images to the list
                Rect R = new Rect(x_coordinate, y_coordinate, chunkWidth, chunkHeight);
                PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
                options.size = new Size(chunkWidth, chunkHeight);
                options.pixelFormat = PixelFormat.ARGB_8888;
                options.editable = true;
                PixelMap pixelMap = PixelMap.create(pixelmap, R, options);
                chunkedImages.add(MergePixelMap.PixelMapSlice.create(chunkWidth, chunkHeight, pixelMap));
                x_coordinate += SQUARE_BLOCK_SIZE;
            }
            y_coordinate += SQUARE_BLOCK_SIZE;
        }
        return chunkedImages;
    }

    /**
     * This method merge all the chunk image list into one single image
     *
     * @param original_height int
     * @param original_width int
     * @param pixelMaps List<MergePixelMap.PixelMapSlice> pixelMaps
     * @return : Merged Image {Bitmap}
     */
    public static PixelMap mergeImage(List<MergePixelMap.PixelMapSlice> pixelMaps, int original_height, int original_width) {

        //Calculating number of Rows and columns of that matrix
        int cols = original_width / SQUARE_BLOCK_SIZE;
        int chunk_width_mod = original_width % SQUARE_BLOCK_SIZE;
        if (chunk_width_mod > 0)
            cols++;
        PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
        options.size = new Size(original_width, original_height);
        options.pixelFormat = PixelFormat.ARGB_8888;
        //options.editable = false;
        return MergePixelMap.getMergePixelmap(pixelMaps, cols, options);
    }

    /**
     * This method converts the byte array to an integer array.
     *
     * @param b byte[]
     * @return : Integer Array
     */

    public static int[] byteArrayToIntArray(byte[] b) {
        int size = b.length / 3;
        System.runFinalization();
        //Garbage collection
        int[] result = new int[size];
        int offset = 0;
        int index = 0;

        while (offset < b.length) {
            result[index++] = byteArrayToInt(b, offset);
            offset = offset + 3;
        }

        return result;
    }

    /**
     * Convert the byte array to an int.
     *
     * @param b byte[]
     * @return Integer
     */
    public static int byteArrayToInt(byte[] b) {
        return byteArrayToInt(b, 0);
    }

    /**
     * Convert the byte array to an int starting from the given offset.
     *
     * @param b byte[]
     * @param offset int
     * @return Integer
     */
    private static int byteArrayToInt(byte[] b, int offset) {

        int value = 0x00000000;

        for (int i = 0; i < 3; i++) {
            int shift = (3 - 1 - i) * 8;
            value |= (b[i + offset] & 0x000000FF) << shift;
        }

        value = value & 0x00FFFFFF;

        return value;
    }

    /**
     * Convert integer array representing [argb] values to byte array
     *
     * representing [rgb] values
     * @param array array
     * @return : byte Array representing [rgb] values.
     */
    public static byte[] convertArray(int[] array) {

        byte[] newarray = new byte[array.length * 3];

        for (int i = 0; i < array.length; i++) {

            newarray[i * 3] = (byte) ((array[i] >> 16) & 0xFF);
            newarray[i * 3 + 1] = (byte) ((array[i] >> 8) & 0xFF);
            newarray[i * 3 + 2] = (byte) ((array[i]) & 0xFF);

        }

        return newarray;
    }

    /**
     * This method is used to check whether the string is empty of not
     *
     * @param str str
     * @return : true or false {boolean}
     */
    public static boolean isStringEmpty(String str) {
        boolean result = true;

        if (str == null) ;
        else {
            str = str.trim();
            if (str.length() > 0 && !str.equals("undefined"))
                result = false;
        }

        return result;
    }
}
